/*
* $Id: uart_fn.c,v 1.51 2007/11/23 08:25:07 sunny Exp $
*/
/*******************************************************************************

       UBEC (Uniband Electronic Corp.)

       Project: U-NET01, Ubiquitous network platform

       File: uart.c

       Version: 0.3.0

       Usage: Uart functions

       Platform: U-NET01 DK with Keil 8051 C compiler

       Reference:

               Silicon Laboratories: C8051F124, C8051F340

               UBEC: UZ2400, UZ2410

       Note :

               Copyright (C) 2007 Uniband Electronic Corporation, All rights reserved

********************************************************************************/
//#if defined(__K0__)
//#pragma section @@DATA @@UART
//#pragma inline  // enable memcpy() and memset() with direct inline expansion instead of function call
//#endif // #if __K0__

#include "unet.h"

#ifdef CONSOLE
//UINT8 UARTWBuffer[128];	// 2007/6/29 11:56, Sunny: unused
UART_INTERFACE UART_BUFF; // Uart receive buffer

#if DEBUG_MODE
    #if defined(__C51__)
	unsigned char bdata putchar_flags;
	sbit dbg_time_stamp_ctrl	= putchar_flags ^ 0;
	sbit print_time_tag		= putchar_flags ^ 1;
	sbit check_CRLF			= putchar_flags ^ 2;
    #else
	BOOL dbg_time_stamp_ctrl;
	BOOL print_time_tag;
	BOOL check_CRLF;
    #endif
#endif // #if DEBUG_MODE

#if SEND_MSG_TIME_STAMP
extern volatile UINT8 MsgLineCnt;
#endif // #if SEND_MSG_TIME_STAMP

// ************************************************************************************************
// ************************************************************************************************
void InitUartVars(void)
{
#if DEBUG_MODE
	dbg_time_stamp_ctrl = 0;
	print_time_tag = 1;
	check_CRLF = 0;
#endif // #if DEBUG_MODE
}

// ************************************************************************************************
// ************************************************************************************************
static void uart_putchar(char c)
{
	volatile UINT8 tmp;
	UARTFIFO *tx = &(UART_BUFF.tx);

	tmp = (tx->rear + 1) & (UART_FIFO_SIZE - 1); // UART_FIFO_SIZE must be power of 2
	while(tmp == tx->front) {	// TX buffer full
	    #if 1
		if(IntStatus.Rx) {	// Received a frame
			unet_msg_dispatch();
		}
	    #endif
	}
	tx->buffer[tx->rear] = c;

	DisInt();
	if(uart_fifo_empty(tx) && !is_uart_tx_busy(&UART_BUFF)) {
		UART_LED_On();
		uart_putc(c);	// kick-off UART Tx
		tx->front = tx->rear = tmp;
		set_uart_tx_busy(&UART_BUFF);
	}
	else
		tx->rear = tmp;
	EnabInt();
}

// ************************************************************************************************
// ************************************************************************************************
#if defined(__C51__)
code
#endif
const char b_ascii[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};	/* Bin -> ASCII */

#define print_hex_nibble(ch)	uart_putchar(b_ascii[ch])

void print_hex(UINT8 c)
{
	print_hex_nibble( (c >>  4) & 0xf );
	print_hex_nibble(  c        & 0xf );
}

void print_ulong(unsigned long value)
{
	unsigned long maxVal = 1000000000;
	char ch;

	do {
		ch = (UINT8)(value / maxVal);
		value %= maxVal;
	} while((maxVal /= 10) && !ch);

	print_hex_nibble(ch);
	while(maxVal) {
		ch = (UINT8)(value / maxVal);
		value %= maxVal;
		maxVal /= 10;
		print_hex_nibble(ch);
	}
}

void print_long(long value)
{
	if(value < 0) {
		uart_putchar('-');
		value = -value;
	}

	print_ulong(value);
}

// ************************************************************************************************
// ************************************************************************************************
#if defined(__C51__)
char putchar(char c)
#else
int putchar(int c)
#endif // #ifdef __K0__
{
#if	SEND_MSG_TIME_STAMP			// 2003/12/24 15:39, Sunny: print debug message timg tag
	if(print_time_tag) {
		union {
			UINT32 dword;
			UINT8 byte[4];
		} timetag;
		UINT8 linecnt;
		UINT8 bytecnt;

		timetag.dword = tick_count;	// 'tick_count' is a free run counter, used as message time tag
		linecnt = MsgLineCnt++;		// 2004/01/13 16:19, Sunny: for debug message time tag, reset every 1ms

//		uart_putchar( '\r' );	// 2003/12/24 19:22, Sunny: the terminal progrsm need to append LF after CR received

	#if BIGENDIAN
		for(bytecnt = 0; bytecnt < 4; ++bytecnt)
	#else
		for(bytecnt = 4; bytecnt-- ; )
	#endif
		{
			print_hex(timetag.byte[bytecnt]);
		}

	#if 1
		uart_putchar( linecnt ? (b_ascii[linecnt]) : '.');
	#else
		uart_putchar( '.' );
		print_hex(linecnt);
		uart_putchar( ':' );
	#endif
		print_time_tag = 0;

		uart_putchar( ' ' );
	}
#endif	// SEND_MSG_TIME_STAMP

#if 0	// convert LF or CR+LF or LF+CR ==> CR only
    #if	SEND_MSG_TIME_STAMP
	if(dbg_time_stamp_ctrl && (c == '\n' || c == '\r')) {
		if(check_CRLF && (check_CRLF != c)) {
			check_CRLF = 0;		// 2003/12/24 14:14, Sunny: CR+LF or LF+CR ==> CR only
		}
		else {
			check_CRLF = c;
			print_time_tag = 1;	// 2003/12/24 15:39, Sunny: print debug message timg tag
			uart_putchar( '\r' );	// 2003/12/24 19:22, Sunny: the terminal progrsm need to append LF after CR received
		}
	}
	else {
		if(check_CRLF)
			check_CRLF = 0;
		uart_putchar( (char)c );
	}
    #else
	uart_putchar( (char)c );
    #endif	// SEND_MSG_TIME_STAMP
#else
    #if	SEND_MSG_TIME_STAMP
	if(dbg_time_stamp_ctrl && (c == '\n' || c == '\r')) {
		print_time_tag = 1;	// 2003/12/24 15:39, Sunny: print debug message timg tag
	}
    #endif	// SEND_MSG_TIME_STAMP

	uart_putchar( (char)c );
#endif // #if 0	// convert LF or CR+LF or LF+CR ==> CR only

	return (c);
}

// ************************************************************************************************
// ************************************************************************************************
int puts(const char *s)
{
	UINT8 len = 0;

	while (*s){
		putchar(*s);
		s++;
		len++;
	}

	return(len);
}

// ************************************************************************************************
// ************************************************************************************************
void putdata(UINT8 UARTWLength, UINT8* DataPtr)
{
	while(UARTWLength--)
		putchar(*DataPtr++);
}

// ************************************************************************************************
// ************************************************************************************************
UINT8 getch(void)
{
	UINT8 c;

	UARTFIFO *rx = &UART_BUFF.rx;

	while(uart_fifo_empty(rx)) {
	        NOP();
	}

	c = *(rx->buffer + rx->front);
	rx->front = (rx->front + 1) & (UART_FIFO_SIZE - 1);

	return(c);
}

// ************************************************************************************************
// ************************************************************************************************
#if DEBUG_MODE		// 2003/10/23 16:04, Sunny: for program tracing/debugging
void print_TRACE_TAG(char const *file, int line)
{
#if 0
	char dbg_msg_buffer[128];

	DBG_puts("\r");
	DBG_puts(file);
	sprintf(dbg_msg_buffer, ", Ln %d: ", line);
	DBG_puts(dbg_msg_buffer);
#else
	puts("\r");
	puts(file);
	printf(", Ln %d: ", line);
#endif
}

// ************************************************************************************************
// ************************************************************************************************
void do_TRACE_TAG(char const *file, int line)
{
	print_TRACE_TAG(file, line);
	puts("TraceTag");
}
#endif // DEBUG_MODE		// 2003/10/23 16:04, Sunny: for program tracing/debugging

// ************************************************************************************************
// ************************************************************************************************
void ProcUartRxData(void)
{
	UINT8 dat;

	while(!UART_BUFF.FLAG && isUartRxDataReady()) {
		dat = getch();
		DBG_printf(("<%d:%02x>", (UINT16)UART_BUFF.STATE, (UINT16)dat));

		switch(UART_BUFF.STATE) {
		default:
			UART_BUFF.STATE = 0;
		case 0:	// wait packet header
			if(dat == 0xCC) {
				UART_BUFF.PTR = 0;
				UART_BUFF.STATE = 1;
			}
			break;
		case 1:	// the packet length
			if(dat >= sizeof(UART_BUFF.UNET_CMD)) {
				if(dat != 0xCC)
					UART_BUFF.STATE = 0;
				break;
			}
			UART_BUFF.CHECKSUM = UART_BUFF.LENGTH = dat;
			UART_BUFF.STATE++;
			break;
		case 2:	// the packet payload
			UART_BUFF.CHECKSUM += dat;
			if(UART_BUFF.PTR < sizeof(UART_BUFF.UNET_CMD))
				UART_BUFF.UNET_CMD[UART_BUFF.PTR++] = dat;
			else {
				UART_BUFF.STATE++;
				putchar(0xEE);	//UART Nak
				TRACE_puts("ProcUartRxData: NAK");
				break;
			}

	#if 1	// no checksum
			if(--UART_BUFF.LENGTH==0){
				UART_BUFF.FLAG = 1;
				UART_BUFF.STATE++;
				putchar(0xFF);	//UART Ack
				TRACE_puts("ProcUartRxData: ACK");
			}
			break;
	#else	// with checksum
			if(--UART_BUFF.LENGTH==0)
				UART_BUFF.STATE++;
			break;
		case 3:	// the packet checksum
			if(UART_BUFF.CHECKSUM == dat) {
				UART_BUFF.FLAG = 1;
				putchar(0xFF);	//UART Ack
			}
			else {
				putchar(0xEE);	//UART Nak
			}
			UART_BUFF.STATE++;
	#endif
		}
	}
}

// ************************************************************************************************
// ************************************************************************************************
#pragma message (__LOC__ "uart_rx_isr() is called by interrupt service routine.")
#if defined(__C51__)
#pragma NOAREGS
#endif
void uart_rx_isr(void)
{
	UARTFIFO *fifo = &(UART_BUFF.rx);
	UINT8 tmp;

	fifo->buffer[tmp = fifo->rear] = uart_getc();
	tmp = (tmp + 1) & (UART_FIFO_SIZE - 1); // UART_FIFO_SIZE must be power of 2
	if(tmp != fifo->front)
	        fifo->rear = tmp;
}

// ************************************************************************************************
// ************************************************************************************************
#pragma message (__LOC__ "uart_tx_isr() is called by interrupt service routine.")
void uart_tx_isr(void)
{
	UARTFIFO *fifo = &(UART_BUFF.tx);

	if(uart_fifo_empty(fifo)) {
		UART_LED_Off();
		clear_uart_tx_busy(&UART_BUFF);
	}
	else {
		uart_putc(*(fifo->buffer + fifo->front));
		fifo->front = (fifo->front + 1) & (UART_FIFO_SIZE - 1);
	}
}

#endif // #ifdef CONSOLE
